﻿using System.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace DimensionsHelper.Common.Utils;

public static class StringHelper
{
    public enum RegexpType
    {
        Normal,
        PartialMatchAll,
        FullMatch
    }

    /// <summary>
    ///     创建数字编号命名字符串，例如：Type1、Type2，有效性由<paramref name="isNameExist" />回调函数保证。
    ///     此函数为null时，将返回 prefix + startNumber。
    /// </summary>
    /// <param name="prefix">前缀字符串</param>
    /// <param name="startNumber">开始数字</param>
    /// <param name="isNameExist">判断名称是否已经存在的回调函数，如果已存在，返回true，否则，返回false</param>
    /// <returns>编号命名字符串</returns>
    public static string BuildName(string prefix, int startNumber, Func<string, bool>? isNameExist = null)
    {
        int i = startNumber;
        string result = prefix + i;
        while (isNameExist != null && isNameExist(result))
        {
            i++;
            result = prefix + i;
        }

        return result;
    }

    /// <summary>
    ///     创建数字编号作为后缀的命名字符串，例如：Type1、Type2，有效性由<paramref name="isNameExist" />回调函数保证。
    /// </summary>
    /// <param name="prefix">数字前缀</param>
    /// <param name="isNameExist">判断名称是否已经存在的回调函数，如果已存在，返回true，否则，返回false</param>
    /// <returns>编号命名字符串</returns>
    public static string BuildName(string prefix, Func<string, bool>? isNameExist)
    {
        return BuildName(prefix, 1, isNameExist);
    }

    /// <summary>
    ///     判断字符串是否是有效数字或者空字符串。
    /// </summary>
    /// <param name="text">待检查的字符串</param>
    /// <returns>如果是空字符或者数字，返回true，否则，返回false</returns>
    public static bool IsStringValidNumberOrEmpty(string text)
    {
        return string.IsNullOrEmpty(text) || (int.TryParse(text, out int val) && val >= 0);
    }

    private static Regex? _escapeRegexExpressionRegex;
        
    private static Regex EscapeRegexExpressionRegex()
    {
        return _escapeRegexExpressionRegex ??= new Regex(@"\\|\[|\]|\(|\)|\.|\?|\||\*|\^|\$|\+|\-|\{|\}");
    }

    private static Regex? _escapeRegexExpressionExceptStarRegex;

    private static Regex EscapeRegexExpressionExceptStarRegex()
    {
        return _escapeRegexExpressionExceptStarRegex ??= new Regex(@"\\|\[|\]|\(|\)|\.|\?|\||\^|\$|\+|\-|\{|\}");
    }

    private static Regex? _escapeRegExpressionExceptStarOrQuestionRegex;

    private static Regex EscapeRegExpressionExceptStarOrQuestionRegex()
    {
        return _escapeRegExpressionExceptStarOrQuestionRegex ??= new Regex(@"\\|\[|\]|\(|\)|\.|\||\^|\$|\+|\-|\{|\}");
    }


    private static Regex? _escapeLocalPathUriRegex;
        
    private static Regex EscapeLocalPathUriRegex()
    {
        return _escapeLocalPathUriRegex ??= new Regex("[&#= ]{1}");
    }

    private static Regex? _unescapeLocalPathUriRegex;
        
    private static Regex UnescapeLocalPathUriRegex()
    {
        return _unescapeLocalPathUriRegex ??= new Regex("(%26)|(%23)|(%3D)|(%20)");
    }


    /// <summary>
    ///     对字符串中的正则表达式操作字符添加转义，变为文本字符。
    /// </summary>
    /// <param name="regExpr">需要转义的字符串</param>
    /// <returns>转义后的字符串</returns>
    public static string EscapeRegExpr(string regExpr)
    {
        return EscapeRegexExpressionRegex().Replace(regExpr, @"\$0");
    }

    /// <summary>
    ///     将正则表达式字符串添加转义，除了"*"。
    /// </summary>
    /// <param name="regExpr">需要添加转义的字符串</param>
    /// <returns>添加转义后的字符串</returns>
    public static string EscapeRegExprExceptStar(string regExpr)
    {
        return EscapeRegexExpressionExceptStarRegex().Replace(regExpr, @"\$0");
    }



    /// <summary>
    ///     将正则表达式字符串添加转义，除了"*"或"?"。
    /// </summary>
    /// <param name="regExpr">需要添加转义的字符串</param>
    /// <returns>添加转义后的字符串</returns>
    public static string EscapeRegExprExceptStarOrQuestion(string regExpr)
    {
        return EscapeRegExpressionExceptStarOrQuestionRegex().Replace(regExpr, @"\$0");
    }

    /// <summary>
    ///     将正则表达式字符串添加转义，除了".*?"。
    /// </summary>
    /// <param name="regExpr">需要添加转义的字符串</param>
    /// <returns>添加转义后的字符串</returns>
    public static string EscapeRegExprExceptDotStarQuestion(string regExpr)
    {
        return EscapeRegExpr(regExpr).Replace(@"\.\*\?", @".*?");
    }

    public static string MakeRegExpr(string input, RegexpType type)
    {
        string result = EscapeRegExpr(input);
        switch (type)
        {
            case RegexpType.Normal:
                return result;
            case RegexpType.PartialMatchAll:
                return $".*?{result}.*?";
            case RegexpType.FullMatch:
                return $"^{result}$";
            default:
                return result;
        }
    }

    public static string MakeRegExpr(IEnumerable<string> inputs, RegexpType type)
    {
        var pattern = new StringBuilder();

        foreach (string input in inputs)
        {
            if (pattern.Length > 0)
            {
                pattern.Append('|');
            }

            pattern.Append(MakeRegExpr(input, type));
        }

        return pattern.ToString();
    }

    /// <summary>
    ///     判断字符串是否是"#NULL!"、"#N/A"、"#VALUE!"、"#REF!"、"#NUM!"、"#NAME?"、"#DIV/0!"中的一个。
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static bool IsExcelError(string input)
    {
        if (string.IsNullOrEmpty(input) || !input.StartsWith("#"))
        {
            return false;
        }

        return input == "#NULL!"
               || input == "#N/A"
               || input == "#VALUE!"
               || input == "#REF!"
               || input == "#NUM!"
               || input == "#NAME?"
               || input == "#DIV/0!";
    }


    /// <summary>
    ///     获取包含指定ARGB值的16进制数字字符串。
    /// </summary>
    /// <param name="a">颜色Alpha值</param>
    /// <param name="r">颜色Red值</param>
    /// <param name="g">颜色Green值</param>
    /// <param name="b">颜色Blue值</param>
    /// <returns>16进制颜色字符串</returns>
    public static string GetArgbString(byte a, byte r, byte g, byte b)
    {
        return $"{a:X2}{r:X2}{g:X2}{b:X2}";
    }


    /// <summary>
    ///     获取包含指定RGB值的16进制数字字符串。
    /// </summary>
    /// <param name="r">颜色Red值</param>
    /// <param name="g">颜色Green值</param>
    /// <param name="b">颜色Blue值</param>
    /// <returns>16进制颜色字符串</returns>
    public static string GetRgbString(byte r, byte g, byte b)
    {
        return $"{r:X2}{g:X2}{b:X2}";
    }


    /// <summary>
    ///     判断给定字符是否是有效的标识符起始字符。
    /// </summary>
    /// <param name="ch"></param>
    /// <returns></returns>
    public static bool IsValidIdentifierStartChar(char ch)
    {
        return char.IsLetter(ch)
               || ch == '_'
               || ch == '$'
               || ch == '@'
               || (ch >= 0x4e00 && ch <= 0x9fbb) // 允许汉字命名
            ;
    }


    /// <summary>
    ///     判断给定字符是否是有效的标识符字符。
    /// </summary>
    /// <param name="ch"></param>
    /// <returns></returns>
    public static bool IsValidIdentifierPartChar(char ch)
    {
        return char.IsNumber(ch) || IsValidIdentifierStartChar(ch);
    }


    /// <summary>
    ///     判断字符串是否是有效的标识符。
    /// </summary>
    /// <param name="name">给定字符串</param>
    /// <returns></returns>
    public static bool IsValidIdentifier(string name)
    {
        if (name.Length == 0)
        {
            return false;
        }

        bool result = IsValidIdentifierStartChar(name[0]);

        if (!result)
        {
            return false;
        }

        for (int i = 1; i < name.Length; i++)
        {
            if (!IsValidIdentifierPartChar(name[i]))
            {
                return false;
            }
        }

        return true;
    }


    /// <summary>
    ///     尝试将字符按照8进制数字解析成整数。注意，需要各个字符是'0'-'7'，不能以'&amp;O'开头。
    /// </summary>
    /// <param name="span">输入字符串</param>
    /// <param name="number">解析结果</param>
    /// <returns>如果解析成功，返回<see langword="true" /></returns>
    public static bool TryParseOctalNumber(this IEnumerable<char> span, out int number)
    {
        number = 0;

        foreach (char c in span)
        {
            if (c < '0' || c > '7')
            {
                return false;
            }

            int digit = c - '0';
            number = digit + (number * 8);
        }

        return false;
    }


    /// <summary>
    ///     尝试将字符按照8进制数字解析成整数。注意，需要各个字符是'0'-'7'，不能以'&amp;O'开头。
    /// </summary>
    /// <param name="input">输入字符串</param>
    /// <param name="number">解析结果</param>
    /// <returns>如果解析成功，返回<see langword="true" /></returns>
    public static bool TryParseOctalNumber(this string input, out int number)
    {
        return input.AsEnumerable().TryParseOctalNumber(out number);
    }
        

    /// <summary>
    ///     将路径中的'&'、'#'和'='替换为转义字符。
    /// </summary>
    public static string EscapeLocalPathUriString(string path)
    {
        return EscapeLocalPathUriRegex().Replace(path, m =>
        {
            switch (m.Value)
            {
                case "&":
                    return "%26";
                case "#":
                    return "%23";
                case " ":
                    return "%20";
                default:
                    return "%3D";
            }
        });
    }



    /// <summary>
    ///     将Uri中的'%26'、'%23'和'%3D'替换为原始字符。
    /// </summary>
    public static string UnescapeLocalPathUriString(string uri)
    {
        return UnescapeLocalPathUriRegex().Replace(uri, m =>
        {
            switch (m.Value)
            {
                case "%26":
                    return "&";
                case "%23":
                    return "#";
                case "%20":
                    return " ";
                default:
                    return "=";
            }
        });
    }


    /// <summary>
    ///     将路径中的'&'、'#'和'='替换为转义字符，并添加<see cref="Uri.UriSchemeFile"/>的头部内容。
    /// </summary>
    /// <param name="path">本地路径</param>
    /// <returns>完整的Uri字符串</returns>
    public static string ToLocalPathUriString(string path)
    {
        // Window路径中允许 #，&，=，需要替换为转义字符。
        return Uri.UriSchemeFile + Uri.SchemeDelimiter + EscapeLocalPathUriString(path);
    }
}